From 018cc8100c47b56296488cc852dd93b9a707607e Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=98yvind=20Kol=C3=A5s?= Date: Sat, 19 Aug 2017 23:08:03 +0200 Subject: [PATCH] babl: add babl_space_rgb_icc --- babl/Makefile.am | 1 + babl/babl-icc.c | 262 +++++++++++++++++++++++++++++++++++++++++++ babl/babl-internal.h | 3 + babl/babl.h | 16 ++- 4 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 babl/babl-icc.c diff --git a/babl/Makefile.am b/babl/Makefile.am index 1a6d38d..403c249 100644 --- a/babl/Makefile.am +++ b/babl/Makefile.am @@ -20,6 +20,7 @@ c_sources = \ babl-fish.c \ babl-format.c \ babl-hash-table.c \ + babl-icc.c \ babl-image.c \ babl-internal.c \ babl-introspect.c \ diff --git a/babl/babl-icc.c b/babl/babl-icc.c new file mode 100644 index 0000000..7fd9b32 --- /dev/null +++ b/babl/babl-icc.c @@ -0,0 +1,262 @@ +#include "config.h" +#include "babl-internal.h" +#include +#include + +#define ICC_HEADER_LEN 128 +#define TAG_COUNT_OFF ICC_HEADER_LEN + +static int load_byte (const char *icc, int offset) +{ + return *(uint8_t*) (&icc[offset]); +} + +static int16_t load_u1Fixed15 (const char *icc, int offset) +{ + return load_byte (icc, offset + 1) + + (load_byte (icc, offset + 0) << 8); +} + +static uint16_t load_uint16 (const char *icc, int offset) +{ + return load_byte (icc, offset + 1) + + (load_byte (icc, offset + 0) << 8); +} + +static double load_s15Fixed16 (const char *icc, int offset) +{ + return load_u1Fixed15 (icc, offset) + load_uint16 (icc, offset + 2) / 65535.0f; +} + +static double load_u16Fixed16 (const char *icc, int offset) +{ + return load_uint16 (icc, offset) + load_uint16 (icc, offset + 2) / 65535.0; +} + +static uint32_t load_uint32 (const char *icc, int offset) +{ + return load_byte (icc, offset + 3) + + (load_byte (icc, offset + 2) << 8) + + (load_byte (icc, offset + 1) << 16) + + (load_byte (icc, offset + 0) << 24); +} + +static void load_sign (const char *icc, int offset, char *sign) +{ + sign[0]=load_byte(icc, offset); + sign[1]=load_byte(icc, offset + 1); + sign[2]=load_byte(icc, offset + 2); + sign[3]=load_byte(icc, offset + 3); + sign[4]=0; +} + +/* looks up offset and length for a specifi icc tag + */ +static int icc_tag (const char *icc, const char *tag, int *offset, int *length) +{ + int tag_count = load_uint32 (icc, TAG_COUNT_OFF); + int profile_size = load_uint32 (icc, 0); + int t; + + for (t = 0; t < tag_count; t++) + { + char tag_signature[5]; + load_sign (icc, TAG_COUNT_OFF + 4 + 12 * t, tag_signature); + if (!strcmp (tag_signature, tag)) + { + if (!offset) + return 1; + *offset = load_uint32 (icc, TAG_COUNT_OFF + 4 + 12* t + 4); + /* avert some potential for maliciousnes.. */ + if (*offset >= profile_size) + { + *offset = profile_size - 1; + } + if (!length) + return 1; + *length = load_uint32 (icc, TAG_COUNT_OFF + 4 + 12* t + 4 * 2); + /* avert some potential for maliciousnes.. */ + if (*offset + *length >= profile_size) + { + *length = profile_size - *offset - 1; + } + return 1; + } + } + return 0; +} + +static const Babl *babl_trc_from_icc (const char *icc, + int length, + char **error) +{ + int offset = 0; + { + int count = load_uint32 (icc, offset + 8); + int i; + { + if (count == 0) + { + return babl_trc_gamma (1.0); + } + else if (count == 1) + { + return babl_trc_gamma (load_byte (icc, offset + 12) + + load_byte (icc, offset + 12 + 1) / 255.0); + } + else + { + return babl_trc_gamma (2.2); + + // XXX: todo implement a curve trc babl type + // as well as detect sRGB curve from LUTs + + for (i = 0; i < count && i < 10; i ++) + { + fprintf (stdout, "%i=%i ", i, load_uint16 (icc, offset + 12 + i * 2)); + if (i % 7 == 0) + fprintf (stdout, "\n"); + } + } + } + } + return NULL; +} + +const Babl * +babl_space_rgb_icc (const char *icc, + int length, + char **error) +{ + int profile_size = load_uint32 (icc, 0); + int icc_ver_major = load_byte (icc, 8); + const Babl *trc_red = NULL; + const Babl *trc_green = NULL; + const Babl *trc_blue = NULL; + char profile_class[5]; + char color_space[5]; + + if (profile_size != length) + { + *error = "icc profile length inconsistency"; + return NULL; + } + if (icc_ver_major > 2) + { + *error = "only ICC v2 profiles supported"; + return NULL; + } + load_sign (icc, 12, profile_class); + if (strcmp (profile_class, "mntr")) + { + *error = "not a monitor-class profile"; + return NULL; + } + load_sign (icc, 16, color_space); + if (strcmp (color_space, "RGB ")) + { + *error = "not defining an RGB space"; + return NULL; + } + { + int offset, element_size; + if (icc_tag (icc, "rTRC", &offset, &element_size)) + { + trc_red = babl_trc_from_icc (icc + offset, element_size, error); + if (*error) + return NULL; + } + if (icc_tag (icc, "gTRC", &offset, &element_size)) + { + trc_green = babl_trc_from_icc (icc + offset, element_size, error); + if (*error) + return NULL; + } + if (icc_tag (icc, "bTRC", &offset, &element_size)) + { + trc_blue = babl_trc_from_icc (icc + offset, element_size, error); + if (*error) + return NULL; + } + } + + if (!trc_red || !trc_green || !trc_blue) + { + *error = "missing TRC"; + return NULL; + } + + if (icc_tag (icc, "chrm", NULL, NULL) && + icc_tag (icc, "wtpt", NULL, NULL)) + { + int offset, element_size; + double redX, redY, greenX, greenY, blueX, blueY; + + icc_tag (icc, "chrm", &offset, &element_size); + { + int channels = load_uint16 (icc, offset + 8); + int phosporant = load_uint16 (icc, offset + 10); + + redX = load_s15Fixed16 (icc, offset + 12); + redY = load_s15Fixed16 (icc, offset + 12 + 4); + greenX = load_s15Fixed16 (icc, offset + 20); + greenY = load_s15Fixed16 (icc, offset + 20 + 4); + blueX = load_s15Fixed16 (icc, offset + 28); + blueY = load_s15Fixed16 (icc, offset + 28 + 4); + + fprintf (stdout, "chromaticity:\n"); + fprintf (stdout, " channels: %i\n", channels); + fprintf (stdout, " phosphorant: %i\n", phosporant); + fprintf (stdout, " CIE xy red: %f %f\n", redX, redY); + fprintf (stdout, " CIE xy green: %f %f\n", greenX, greenY); + fprintf (stdout, " CIE xy blue: %f %f\n", blueX, blueY); + } + + icc_tag (icc, "wtpt", &offset, &element_size); + { + double wX = load_u16Fixed16 (icc, offset + 8); + double wY = load_u16Fixed16 (icc, offset + 8 + 4); + double wZ = load_u16Fixed16 (icc, offset + 8 + 4 * 2); + + return babl_space_rgb_chromaticities (NULL, + wX / (wX + wY + wZ), + wY / (wX + wY + wZ), + redX, redY, + greenX, greenY, + blueX, blueY, + trc_red, trc_green, trc_blue); + + } + } + else if (icc_tag (icc, "rXYZ", NULL, NULL) && + icc_tag (icc, "gXYZ", NULL, NULL) && + icc_tag (icc, "bXYZ", NULL, NULL)) + { + int offset, element_size; + double rx, gx, bx; + double ry, gy, by; + double rz, gz, bz; + + icc_tag (icc, "rXYZ", &offset, &element_size); + rx = load_u16Fixed16 (icc, offset + 8 + 4 * 0); + ry = load_u16Fixed16 (icc, offset + 8 + 4 * 1); + rz = load_u16Fixed16 (icc, offset + 8 + 4 * 2); + icc_tag (icc, "gXYZ", &offset, &element_size); + gx = load_u16Fixed16 (icc, offset + 8 + 4 * 0); + gy = load_u16Fixed16 (icc, offset + 8 + 4 * 1); + gz = load_u16Fixed16 (icc, offset + 8 + 4 * 2); + icc_tag (icc, "bXYZ", &offset, &element_size); + bx = load_u16Fixed16 (icc, offset + 8 + 4 * 0); + by = load_u16Fixed16 (icc, offset + 8 + 4 * 1); + bz = load_u16Fixed16 (icc, offset + 8 + 4 * 2); + + return babl_space_rgb_matrix (NULL, + rx, gx, bx, + ry, gy, by, + rz, gz, bz, + trc_red, trc_green, trc_blue); + } + + *error = "didnt find RGB primaries"; + return NULL; +} diff --git a/babl/babl-internal.h b/babl/babl-internal.h index 9181bd9..33132e6 100644 --- a/babl/babl-internal.h +++ b/babl/babl-internal.h @@ -392,6 +392,9 @@ float babl_trc_to_linearf (const Babl *trc, float value); void babl_space_to_xyz (const Babl *space, const double *rgb, double *xyz); void babl_space_from_xyz (const Babl *space, const double *xyz, double *rgb); +const Babl *babl_space_from_icc (const char *icc, + int length, + char **error); #endif diff --git a/babl/babl.h b/babl/babl.h index 31d58dc..4edec9a 100644 --- a/babl/babl.h +++ b/babl/babl.h @@ -123,7 +123,7 @@ const Babl * babl_space_rgb_chromaticities (const char *name, const Babl *trc_blue); /** - * babl_space_rgb_chromaticities: + * babl_space_rgb_matrix: * * Creates a new RGB matrix color space definition using a precomputed * D50 adapted 3x3 matrix, as possibly read from an ICC profile. @@ -136,6 +136,20 @@ babl_space_rgb_matrix (const char *name, const Babl *trc_red, const Babl *trc_green, const Babl *trc_blue); +/** + * babl_space_rgb_icc: + * + * Create a babl space from an in memory ICC profile, the + * profile does no longer need to be loaded for the space to work, + * multiple calls with the same icc profile will result in the + * same space. + * + * If a BablSpace cannot be created from the profile NULL is returned and + * a static string is set on the provided error location. + */ +const Babl *babl_space_rgb_icc (const char *icc, + int length, + char **error); const double * babl_space_get_rgbtoxyz (const Babl *space); void babl_space_to_xyz (const Babl *space, const double *rgb, double *xyz); -- 2.30.2